import Vue from 'vue'
import util from './util.js'
import YouonBle from './YouonBle.js'
import {
	getLockUpgradeMessage
} from '@/api/lock.js'
import crc from './crc32.js';
const osName = Vue.prototype.$sysInfo.osName;
class BleUpgrade {
	youonBle = null; // 门锁类的实例
	dfuBle = null; // DFU升级的工具类
	lockNo = ''; // 门锁ID
	mobile = ''; // 用户手机号	
	deviceId = '';
	downLoadPackUrl = '';
	cmdType = 1; // DFU的select命令的type参数  1:表示Command object(发送到Control point) 2:Data object(发送到Data point)
	totalPage = 0; // bin_data的数据/4096(4KB)+1   totalPage:表示bin_data 分成4KB 一个page 总共有多少page
	curPage = 0; // 当前已经发送到第几页
	bin_offset = 0; // bin数据包的发送偏移量
	binByteLength = 0; // bin数据包总字节数
	dat_data = ''; // bat文件数据
	bin_data = ''; // bin文件数据
	change_dfu_cmd = ''; // 向锁发送命令 切换DFU模式	
	progressFunc = null; // 进度调用函数
	successFunc = null; // 升级成功函数
	errFunc = null; // 失败的回调
	curWriteBin = ''; // 当前正在写的bin数据
	remainWriteBin = ''; // 有时传输4096个字节 设备响应通常没有4096个字节 校验不通过 还需要写的字节数

	isReciveBleData = false;
	constructor({
		downLoadPackUrl,
		lockNo,
		mobile,
		mac,
		progressFunc,
		successFunc,
		errFunc
	}) {
		this.downLoadPackUrl = downLoadPackUrl;
		this.progressFunc = progressFunc;
		this.successFunc = successFunc;
		this.errFunc = errFunc;
		this.lockNo = lockNo;
		this.mobile = mobile;
		this.deviceId = util.strInsert(mac, 2);
	}
	async startUpgrade() {
		try {
			await this.downFirmware();
			await this.unzip(this.lockNo);
			await this.getUpgradeCmdMsg();
			this.upgradeLock(); // 开始升级
		} catch (e) {
			this.errFunc(e)
		}
	}
	downFirmware() {
		this.progressFunc('下载升级包');
		const filemgr = wx.getFileSystemManager();
		const targetPath = wx.env.USER_DATA_PATH;
		return new Promise((resolve, reject) => {
			wx.downloadFile({
				url: this.downLoadPackUrl,
				success: (res) => {
					console.log('下载res', res)
					filemgr.saveFile({
						tempFilePath: res.tempFilePath,
						// filePath: targetPath + "/firmware.zip",
						filePath: targetPath + "/" + this.lockNo + ".zip",
						success: (result) => {
							resolve();
						},
						fail: (e) => {
							reject('保存文件失败!')
						}
					})
				}
			})
		})
	}
	unzip(lockNo) {
		this.progressFunc('解压升级包');
		const filemgr = wx.getFileSystemManager()
		const targetPath = wx.env.USER_DATA_PATH
		console.log('路径' + targetPath + "/" + lockNo + ".zip")
		return new Promise((resolve, reject) => {
			filemgr.unzip({
				zipFilePath: targetPath + "/" + lockNo + ".zip",
				targetPath: targetPath,
				success: (unzipRes) => {
					console.log("解压成功:" + JSON.stringify(unzipRes))
					filemgr.readFile({
						filePath: targetPath + '/manifest.json',
						encoding: 'binary',
						complete: (readFileRes) => {
							console.log('读取压缩包文件返回 ', readFileRes);
							const data = JSON.parse(readFileRes.data);
							const application = data.manifest.application;
							console.log("dat_file文件路径为", targetPath + '/' + application.dat_file)
							console.log("bin_file文件路径为", targetPath + '/' + application.bin_file)
							const readDatData = new Promise((resolve, reject) => {
								//bat文件的解析
								filemgr.readFile({
									filePath: targetPath + '/' + application.dat_file,
									complete: (datRes) => {
										const dat_data = util.ab2hex(datRes.data)
										this.dat_data = datRes.data
										console.log('dat data=>', dat_data, ' byteLength==>', datRes.data.byteLength)
										resolve();
									}
								})
							})
							const readBinData = new Promise((resolve, reject) => {
								//bin文件的解析
								filemgr.readFile({
									filePath: targetPath + '/' + application.bin_file,
									complete: (binRes) => {
										this.curPage = 0; // 初始化
										this.bin_offset = 0;
										this.bin_data = binRes.data;
										this.binByteLength = this.bin_data.byteLength;
										this.totalPage = Math.ceil(this.binByteLength / 4096);
										console.log('bin byteLength=>', this.binByteLength);
										resolve();
									}
								})
							})
							Promise.all([readDatData, readBinData]).then(() => {
								resolve();
							}).catch((err) => {
								reject('解压文件失败!');
							})
						}
					})
				},
				fail: function(unzipRes) {
					console.log('解压失败' + unzipRes.errMsg)
					reject('解压失败' + unzipRes.errMsg)
				}
			})
		})

	}
	getUpgradeCmdMsg() {
		this.progressFunc('获取升级报文');
		return new Promise(async (resolve, reject) => {
			let data = {
				lockNo: this.lockNo, //this.upgradeList[this.index].lockNo,
				mobile: this.mobile,
			}
			const res = await getLockUpgradeMessage(data)
			if (res && res.statecode == 0) {
				console.log('升级报文', res);
				this.change_dfu_cmd = res.data.taskMessage.replace(" ", "");
				resolve();
			} else {
				reject('获取报文失败')
			}
		})
	}
	async upgradeLock() {
		try {
			this.isReciveBleData = false;
			this.progressFunc('开始升级');
			this.youonBle = new YouonBle();
			if (osName == 'ios') {
				await this.youonBle.connBleAndNotifyForIos(this.deviceId);
			} else {
				await this.youonBle.connBleAndNotifyForAndroid(this.deviceId);
			}
			console.log('......onBLECharacteristicValueChange')
			this.youonBle.onBLECharacteristicValueChange((res) => {
				this.receiveBleData(res);
			});
			this.youonBle.sendData(this.change_dfu_cmd, this.change_dfu_cmd.length / 2);
		} catch (err) {
			console.log('upgradeLock err', err);
			this.errFunc(err.msg || '未知错误')
		}
	}
	async receiveBleData(res) {
		console.log('处于DFU模式');
		if (this.isReciveBleData) {
			return;
		}
		this.isReciveBleData = true;
		try {
			/**
			 * 为了兼容红米手机  断开连接后 必须关闭蓝牙设备器   然后 等待200毫秒 再打开蓝牙适配器 然后获取权限 然后 再连接 
			 * 红米等其他安卓真垃圾
			 */
			await this.youonBle.closeBLEConnection();
			await this.youonBle.closeBluetoothAdapter();
			this.dfuBle = new YouonBle();
			await this.dfuBle.connDFUAndNotify(this.deviceId);
			this.dfuBle.onBLECharacteristicValueChange((res) => {
				this.dataEventHandler(res)
			});
			const buffer = new ArrayBuffer(2);
			this.cmdType = 1;
			this.dfuBle.send_select_cmd_obj_06();
		} catch (err) {
			console.log('receiveBleData err', err)
			this.errFunc(err.msg || 'DFU模式开启失败!')
		}
	}
	async dataEventHandler(res) {
		try {
			console.log('receive DFU res ', JSON.stringify(res))
			const response = util.hexToBytes(res);
			const parseResp = util.parseResponse(response);
			console.log('parseResp===> ', JSON.stringify(parseResp));
			const respOpCode = parseResp.responseOpCode;
			switch (respOpCode) {
				case 0x01: // create
					console.log('-----response 0x01 create')
					if (this.cmdType == 1) {
						const datSize = this.dat_data.byteLength;
						await this.dfuBle.writeDatData(this.dat_data, datSize, 0); // 向90002写入数据
						this.dfuBle.send_checksum_03();
					} else {
						if (this.remainWriteBin) {
							this.dfuBle.writeBinData(this.remainWriteBin, this.remainWriteBin.byteLength, 0) // 向90002写入没到4096个字节的剩余字节数据
						} else {
							// 发送bin数据,没接收4KB数据,会回复一次CRC校验值,直至整个新固件image接收完毕
							const cutSize = this.binByteLength - this.bin_offset > 4096 ? 4096 : this.binByteLength - this.bin_offset;
							const buffer = this.curWriteBin = this.bin_data.slice(this.bin_offset, this.bin_offset + cutSize); // 4KB数据
							const binSize = buffer.byteLength;
							this.dfuBle.writeBinData(buffer, binSize, 0); // 向90002写入数据
						}
					}
					break;
				case 0x03: // CALCULATE CHECKSUM
					console.log('-----response 0x03 CALCULATE CHECKSUM')
					if (this.cmdType == 1) {
						this.dfuBle.send_execute_04();
					} else {
						this.bin_offset = parseResp.data.offset;
						this.curPage = Math.ceil(this.bin_offset / 4096); // 看下已经发到第几页														
						console.log('curPage=>', this.curPage, ' bin_offset=>', this.bin_offset, ' binByteLength=>', this.binByteLength)
						// // 下面开始校验crc
						const crcStarIdx = this.curPage * 4096;
						console.log('resp crc32->', parseResp.data.crc32, ' crc32->', crc.crc32(this.bin_data.slice(0, crcStarIdx)))
						if (parseResp.data.crc32 != crc.crc32(this.bin_data.slice(0, crcStarIdx))) {
							// const remainLength = this.curPage != this.totalPage ? this.curPage * 4096 - this.bin_offset : this.binByteLength - this.bin_offset;
							const remainLength = this.curPage * 4096 - this.bin_offset;
							if (remainLength > 0) {
								console.log('remainLength---->', remainLength)
								this.remainWriteBin = this.curWriteBin.slice(this.bin_offset);
								if (remainLength < 0x1000) {
									this.dfuBle.send_create_data_obj_01(util.formater4Length(remainLength));
								} else {
									this.dfuBle.send_create_data_obj_01(util.formater4Length(0x1000));
								}
								return;
							}
						} else {
							this.remainWriteBin = '';
						}
						const percent = ((this.bin_offset / this.binByteLength) * 100).toFixed(2);
						let msg = '已发送' + percent + '%';
						this.progressFunc(msg);
						this.dfuBle.send_execute_04();
					}
					break;
				case 0x04: // execute
					console.log('-----response 0x04 execute')
					if (this.cmdType == 1) {
						this.cmdType = 2; // 2表示开始发送升级包的bin包数据了
						await this.dfuBle.send_select_data_obj_06();
					} else {
						if (this.curPage != this.totalPage) { // 还没发送完
							const remainLength = this.bin_data.byteLength - this.bin_offset;
							console.log('校验数据中:' + this.curPage + '/' + this.totalPage, ' 剩余字节数:', remainLength)
							if (remainLength < 0x1000) {
								this.dfuBle.send_create_data_obj_01(util.formater4Length(remainLength));
							} else {
								this.dfuBle.send_create_data_obj_01(util.formater4Length(0x1000));
							}
						} else { // 到这里就算升级成功了
							// 将一些参数恢复至默认值
							this.cmdType = 1;							
							this.successFunc();
						}
					}
					break;
				case 0x06: // select
					const respOffset = parseResp.data.offset;
					const respCrc32 = parseResp.data.crc32;
					if (this.cmdType == 1) { // 如果cmdType是1 表示是Control point的操作返回
						// 如果返回的数据偏移量=要发送的dat包的字节长度 那么说明bat包已经发送成功了
						// 直接发送执行命令
						if (respOffset == this.dat_data.byteLength) {
							this.dfuBle.send_execute_04();
						} else { // 发送创建命令
							this.dfuBle.send_create_cmd_obj_01();
						}
					} else {
						console.log('-----断点续传 case 0x06 select respOffset--->', respOffset)
						/**
						 * 打包的版本 没有使用断点续传  直接每次重新传
						 */
						if (respOffset == 0 || respOffset > 0) {
							// console.log('-----create data object----');
							this.dfuBle.send_create_data_obj_01(util.formater4Length(0x1000));
							return;
						}
						// 直接发送执行命令
						this.dfuBle.send_execute_04();
					}
					break;
				default:
					this.errFunc('未知的操作Code:' + respOpCode)
					break;
			}
		} catch (e) {
			const errMsg = '';
			if (typeof e == 'object') {
				console.log('dataEventHandler err', e)
			} else {
				console.log('dataEventHandler err', e)
			}
			this.errFunc('操作中心返回失败!')
		}
	}

	disConnAndCloseBluetooth() {
		if (this.youonBle) this.youonBle.disConnAndClose();
		if (this.dfuBle) this.dfuBle.disConnAndClose();
	}
}
export default BleUpgrade;